package com.gis3c.datacenter.service.impl;

import com.gis3c.common.exception.BusinessException;
import com.gis3c.datacenter.dao.EnterpriseDao;
import com.gis3c.datacenter.entry.Enterprise;
import com.gis3c.datacenter.entry.WasteGasEnterprise;
import com.gis3c.datacenter.entry.WasteWaterEnterprise;
import com.gis3c.datacenter.service.EnterpriseService;
import com.gis3c.online.dao.PollutionSourceDao;
import com.gis3c.online.entry.PollItem;
import com.gis3c.online.entry.PollutionSourceStation;
import com.gis3c.spatial.common.FeatureUtilities;
import com.gis3c.spatial.common.GeometryUtilities;
import com.vividsolutions.jts.geom.Geometry;
import com.vividsolutions.jts.geom.Point;
import com.vividsolutions.jts.geom.Polygon;
import org.geotools.data.simple.SimpleFeatureCollection;
import org.geotools.data.simple.SimpleFeatureIterator;
import org.geotools.filter.text.cql2.CQLException;
import org.opengis.feature.simple.SimpleFeature;
import org.opengis.referencing.FactoryException;
import org.opengis.referencing.operation.TransformException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;
import java.util.stream.Collectors;

/**
 * Created by hukekuan on 2018/3/28.
 */
@Service
@Transactional(readOnly = true, rollbackFor = Exception.class)
public class EnterpriseServiceImpl implements EnterpriseService {
    @Autowired
    private EnterpriseDao enterpriseDao;
    @Autowired
    private PollutionSourceDao pollutionSourceDao;

    @Override
    public String findEnterpriseList(Map<String,Object> queryParams) {
        String result = null;
        List<Enterprise> enterpriseList = enterpriseDao.findEnterpriseList(queryParams);
        if(enterpriseList != null && enterpriseList.size() > 0){
            List<PollutionSourceStation> psList = null;
            Set<String> onlineCodes;
            Set<PollutionSourceStation> psStations = null;
            Set<PollItem> pollItems;
            String itemLevel;

            onlineCodes = enterpriseList
                    .stream()
                    .filter(enterprise -> enterprise.getOnLineCode() != null
                            && !"".equals(enterprise.getOnLineCode())
                            && !"否".equals(enterprise.getOnLineCode()))
                    .map(enterprise -> enterprise.getOnLineCode()).collect(Collectors.toSet());
            //根据在线企业编号获取在线站点列表
            if(onlineCodes != null && onlineCodes.size() > 0){
                psList = pollutionSourceDao.findStationByEnterpriseCode(onlineCodes);
            }
            
            for(Enterprise enterprise: enterpriseList){
                //根据在线企业编号获取关联的站点信息
                if(psList != null){
                    psStations = psList.stream()
                            .filter(pollutionSourceStation -> pollutionSourceStation.getEnterpriseCode().equals(enterprise.getOnLineCode()))
                            .collect(Collectors.toSet());
                }

                //设置企业超标信息
                enterprise.setOverStand("否");
                if(psStations != null){
                    for(PollutionSourceStation pollutionSourceStation : psStations){
                        if("是".equals(enterprise.getOverStand())) break;
                        pollItems = pollutionSourceStation.getPollItems();
                        if(pollItems != null){
                            for(PollItem pollItem:pollItems){
                                itemLevel = pollItem.getLevel();
                                if(itemLevel != null && !"".equals(itemLevel) && !"0".equals(itemLevel)){
                                    enterprise.setOverStand("是");
                                    break;
                                }
                            }
                        }
                    }
                }
                //设置空间字段
                enterprise.setGeometry(GeometryUtilities.CreatePoint(enterprise.getLongitude(),enterprise.getLatitude()));
            }
        }

        try{
            result = FeatureUtilities.JavaBeans2Json(enterpriseList);
        }catch (IllegalAccessException | IOException e) {
            throw new BusinessException("AirStation实体转GeoJSON出错");
        }
        return result;
    }

    @Override
    public String findWasteGasEnterpriseList() {
        String result = null;
        List<WasteGasEnterprise> enterprises =  enterpriseDao.findWasteGasEnterpriseList();
        enterprises.forEach(enterprise -> enterprise.setGeometry(
                GeometryUtilities.CreatePoint(enterprise.getLongitude(),enterprise.getLatitude()))
        );
        try{
            result = FeatureUtilities.JavaBeans2Json(enterprises);
        }catch (IllegalAccessException | IOException e) {
            throw new BusinessException("AirStation实体转GeoJSON出错");
        }
        return result;
    }

    @Override
    public Map<String,Object> findBufferedEnterprises(
            Double startLon, Double startLat, Double endLon, Double endLat, Double bufferDistance, Double spatialDis) {
        Map<String,Object> result = new HashMap<>();
        //获取企业信息
        List<WasteGasEnterprise> enterprises =  enterpriseDao.findWasteGasEnterpriseList();
        //企业对象设置空间字段
        enterprises.forEach(enterprise -> enterprise.setGeometry(
                GeometryUtilities.CreatePoint(enterprise.getLongitude(),enterprise.getLatitude()))
        );
        try{
            SimpleFeatureCollection queryCollection,bufferCollection,resultCollection;
            //企业列表转SimpleFeatureCollection
            queryCollection = FeatureUtilities.JavaBeans2Collections(enterprises);
            //生成起始点
            Point startPoint =GeometryUtilities.CreatePoint(startLon,startLat);
            Geometry bufferGeometry = null;
            if(endLon != null && endLat != null){
                bufferGeometry = GeometryUtilities.CreateLineStringByPoints(new Point[]{
                        startPoint, GeometryUtilities.CreatePoint(endLon,endLat)
                });
            }else {
                bufferGeometry = startPoint;
            }

            Polygon bufferPolygon = GeometryUtilities.CreateBuffer(bufferGeometry,spatialDis);
            //利用Buffer对象对企业SimpleFeatureCollection做空间查询
            bufferCollection = FeatureUtilities.FeatureCollectionFilter(queryCollection,bufferPolygon,null);
            SimpleFeature simpleFeature;
            Double pointDistance;
            Set<String> onlineCodes = new HashSet<>();
            String onlineCode;
            try(SimpleFeatureIterator featureIterator =  bufferCollection.features()){
                while(featureIterator.hasNext()){
                    simpleFeature = featureIterator.next();
                    //计算每个企业点与中心点的距离
                    pointDistance = GeometryUtilities.PointDistance(startPoint, (Point) simpleFeature.getDefaultGeometry());
                    simpleFeature.setAttribute("distance",pointDistance);
                    //获取Buffer范围内企业的在线企业的编号（如果有的话）
                    onlineCode = simpleFeature.getAttribute("onLineCode") == null ?
                            "" : simpleFeature.getAttribute("onLineCode").toString();
                    if(!"".equals(onlineCode) && !"否".equals(onlineCode) && pointDistance <= bufferDistance){
                        onlineCodes.add(onlineCode);
                    }
                }
            }
            //根据距离做企业的二次筛选
            resultCollection = FeatureUtilities.FeatureCollectionFilter(bufferCollection,null,"distance <= " + bufferDistance);
            //根据在线企业编号获取在线站点列表
            List<PollutionSourceStation> psList = pollutionSourceDao.findStationByEnterpriseCode(onlineCodes);
            Set<PollItem> pollItems;
            String itemLevel;
            try (SimpleFeatureIterator featureIterator =  resultCollection.features()){
                while(featureIterator.hasNext()){
                    simpleFeature = featureIterator.next();
                    //通过站点信息为企业的isOverStand设置属性
                    for(PollutionSourceStation pollutionSourceStation : psList){
                        if(pollutionSourceStation.getEnterpriseCode().equals(simpleFeature.getAttribute("onLineCode"))){
                            simpleFeature.setAttribute("overStand","否");
                            pollItems = pollutionSourceStation.getPollItems();
                            if(pollItems != null){
                                for(PollItem pollItem:pollItems){
                                    itemLevel = pollItem.getLevel();
                                    if(itemLevel != null && !"".equals(itemLevel) && !"0".equals(itemLevel)){
                                        simpleFeature.setAttribute("overStand","是");
                                        break;
                                    }
                                }
                            }
                        }
                    }
                }
            }
            result.put("enterpriseList",FeatureUtilities.FeatureCollection2GeoJson(resultCollection));
            result.put("bufferWKT",GeometryUtilities.Geometry2Wkt(bufferPolygon));
        }catch (IllegalAccessException | IOException | CQLException | TransformException | FactoryException e) {
            throw new BusinessException("AirStation实体转GeoJSON出错");
        }
        return result;
    }

    @Override
    public String findWasteWaterorPlantEnterpriseList(Map<String,Object> queryParams, Map<String,Object> itemParams) {
        String result = null;
        List<WasteWaterEnterprise> enterprises
                =  enterpriseDao.findWasteWaterorPlantEnterpriseList(queryParams);
        enterprises = bindOnlineForEnterprises(enterprises,itemParams);


        try{
            result = FeatureUtilities.JavaBeans2Json(enterprises);
        }catch (IllegalAccessException | IOException e) {
            throw new BusinessException("AirStation实体转GeoJSON出错");
        }
        return result;
    }

    @Override
    public String findTreatmentPlantEnterpriseList() {
        String result = null;
        List<WasteWaterEnterprise> enterprises =  enterpriseDao.findTreatmentPlantEnterpriseList();
        enterprises.forEach(enterprise -> enterprise.setGeometry(
                GeometryUtilities.CreatePoint(enterprise.getLongitude(),enterprise.getLatitude()))
        );
        try{
            result = FeatureUtilities.JavaBeans2Json(enterprises);
        }catch (IllegalAccessException | IOException e) {
            throw new BusinessException("AirStation实体转GeoJSON出错");
        }
        return result;
    }

    @Override
    public Set<String> findOnlineCodes(Integer subType) {
        return enterpriseDao.findOnlineCodes(subType);
    }

    private List<? extends Enterprise> addOnlineInfo(List<? extends Enterprise> enterprises){
        return null;
    }


    /**
     * 根据在线企业编号给企业绑定在线监测项目信息及空间字段
     * @param enterpriseList
     * @param <T>
     * @param itemOptions 监测项目筛选信息，如果为null不显示
     * @return
     */
    private <T extends Enterprise> List<T> bindOnlineForEnterprises(List<T> enterpriseList, Map<String,Object> itemOptions){
        if(enterpriseList != null && enterpriseList.size() > 0){
            List<PollutionSourceStation> psList = null;
            Set<String> onlineCodes;
            Set<PollutionSourceStation> psStations = null;
            Set<PollItem> pollItems;
            long overStandCount;
            Boolean queryResult;
            Set<String> queryEnterpriseCodes = null;

            if(itemOptions != null && itemOptions.keySet().size() > 0){
                queryEnterpriseCodes = new HashSet<>();
            }

            //获取数据中心企业中的在线企业编号
            onlineCodes = enterpriseList
                    .stream()
                    .filter(enterprise -> enterprise.getOnLineCode() != null
                            && !"".equals(enterprise.getOnLineCode())
                            && !"否".equals(enterprise.getOnLineCode()))
                    .map(enterprise -> enterprise.getOnLineCode()).collect(Collectors.toSet());

            //根据在线企业编号获取在线站点列表
            if(onlineCodes != null && onlineCodes.size() > 0){
                psList = pollutionSourceDao.findStationByEnterpriseCode(onlineCodes);
            }

            for(T enterprise: enterpriseList){
                //根据在线企业编号获取关联的站点信息
                if(psList != null){
                    psStations = psList.stream()
                            .filter(pollutionSourceStation -> pollutionSourceStation.getEnterpriseCode().equals(enterprise.getOnLineCode()))
                            .collect(Collectors.toSet());
                }

                //设置企业超标信息
                enterprise.setOverStand("否");
                if(psStations != null && psStations.size() > 0){
                    for(PollutionSourceStation pollutionSourceStation : psStations){
                        pollItems = pollutionSourceStation.getPollItems();
                        if(pollItems != null && pollItems.size() > 0){
                            //筛选监测项目
                            if(queryEnterpriseCodes != null){
                                queryResult = filterStationByItem(pollItems,itemOptions);
                                if(queryResult && !queryEnterpriseCodes.contains(enterprise.getEnterpriseCode())){
                                    queryEnterpriseCodes.add(enterprise.getEnterpriseCode());
                                }
                            }
                            //判断站点是否超标
                            if("是".equals(enterprise.getOverStand())) break;
                            overStandCount = pollItems.stream()
                                    .filter(pollItem -> pollItem.getLevel() != null
                                            && !"".equals(pollItem.getLevel())
                                            && !"0".equals(pollItem.getLevel()))
                                    .count();
                            if(overStandCount > 0){
                                enterprise.setOverStand("是");
                            }
                        }
                    }
                }

                //设置空间字段
                enterprise.setGeometry(GeometryUtilities.CreatePoint(enterprise.getLongitude(),enterprise.getLatitude()));
            }
            //删除被筛查掉的企业
            if(queryEnterpriseCodes != null){
                Set<String> finalQueryEnterpriseCodes = queryEnterpriseCodes;
                enterpriseList = enterpriseList.stream()
                        .filter(enterprise -> finalQueryEnterpriseCodes.contains(enterprise.getEnterpriseCode()))
                        .collect(Collectors.toList());
            }
        }
        return enterpriseList;
    }

    /**
     * 利用监测项目筛选站点
     * @param pollItems
     * @param itemOptions
     * @return
     */
    private Boolean filterStationByItem(Set<PollItem> pollItems,Map<String,Object> itemOptions){
        PollItem queryItem;
        Double itemValue,comparedValue;
        Boolean queryResult = null;
        if(pollItems != null && itemOptions != null && itemOptions.keySet().size() > 0){
            queryItem = pollItems.stream()
                    .filter(pollItem -> itemOptions.get("itemCode").toString().equals(pollItem.getItemCode()))
                    .findFirst().get();
            if(queryItem != null){
                itemValue = queryItem.getIetemValue() == null || "".equals(queryItem.getIetemValue())
                        ? 0:Double.parseDouble(queryItem.getIetemValue());
                comparedValue = (Double) itemOptions.get("value");
                switch (itemOptions.get("state").toString()){
                    case "gt":
                        queryResult = itemValue > comparedValue;
                        break;
                    case "lt":
                        queryResult = itemValue < comparedValue;
                        break;
                    case "ge":
                        queryResult = itemValue >= comparedValue;
                        break;
                    case "le":
                        queryResult = itemValue <= comparedValue;
                        break;
                    default:
                        queryResult = false;
                        break;
                }
            }
        }
        return queryResult;
    }
}
